﻿using Grow.Domain.EventBus;
using Grow.EventBus.Cap.PublisherDomainEvents;
using Grow.EventBus.Cap.SubscriberDomainEvents;
using Grow.Module.Service;
using Grow.Scheduler.Job;

namespace Grow.EventBus.Cap.Jobs
{
    public class RetrySubscriberDomainEventJob : JobBase
    {

        private readonly DomainEventBusOptions _eventBusOptions;
        private readonly ISubscriberDomainEventHandler _subscriberDomainEventHandler;
        private readonly ISubscriberDomainEventRepository _subscriberDomainEventRepository;
        public RetrySubscriberDomainEventJob(IServiceContext serviceContext) : base(serviceContext)
        {
            _eventBusOptions = serviceContext.GetOptions<DomainEventBusOptions>();

            _subscriberDomainEventHandler = _serviceContext.GetRequiredService<ISubscriberDomainEventHandler>();

            _subscriberDomainEventRepository = serviceContext.GetRequiredService<ISubscriberDomainEventRepository>();
        }


        public override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            var subscriberDomainEventDtos = await GetSubscriberDomainEventsOfNeedRetryAsync(cancellationToken);
            if (subscriberDomainEventDtos.Count == 0)
            {
                return;
            }
            Parallel.ForEach(subscriberDomainEventDtos, subscriberDomainEventDto =>
            {
                _ = Task.Factory.StartNew(async () =>
                {
                    await _subscriberDomainEventHandler.SubscriberAsync(subscriberDomainEventDto, cancellationToken);
                }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
            });
        }

        private async Task<List<SubscriberDomainEventDto>> GetSubscriberDomainEventsOfNeedRetryAsync(CancellationToken cancellationToken)
        {
            var now = DateTimeOffset.Now;
            var sendAt = now.AddSeconds(-_eventBusOptions.StartRetryAfter);


            var subscriberEventDataEntitys= await _subscriberDomainEventRepository.ToListAsync(a =>
                a.SendAt < sendAt
                && a.EnvironmentName == _eventBusOptions.EnvironmentName
                && a.RetryCount < _eventBusOptions.RetryLimitCount
                && (a.LockEndTime < now || a.LockEndTime == null)
                && (a.EventStatus == SubscriberDomainEventStatus.Wait || a.EventStatus == SubscriberDomainEventStatus.Fail)
                ,_eventBusOptions.RetryFailedMax,cancellationToken);

            return subscriberEventDataEntitys.Select(a => new SubscriberDomainEventDto(
                    a.Id,
                    a.EventName,
                    a.EventType,
                    a.EventHandlerType,
                    a.EventBody,
                    a.EventStatus,
                    a.RetryCount,
                    a.SendAt,
                    a.LockEndTime,
                    a.ExpireTime)).ToList();
        }
    }
}
